home *** CD-ROM | disk | FTP | other *** search
- /*
- File: Futures.c
-
- Contains: xxx put contents here xxx
-
- Written by: xxx put writers here xxx
-
- Copyright: © 1993 by Apple Computer, Inc., all rights reserved.
- © 1993-1994 by Steve Sisak, all rights reserved.
-
- Change History (most recent first):
-
- <1> 10/14/93 bsp first checked in
-
- */
-
- #include "Futures.h"
- #include <Threads.h>
- #include <GestaltEqu.h>
-
- pascal OSErr IsFutureBlock(AppleEvent* message);
- pascal OSErr DoThreadBlock(AppleEvent* message);
- pascal OSErr DoThreadUnblock(AppleEvent* message);
-
- #define kAERefconAttribute 'refc'
- #define kAENonexistantAttribute 'gag!'
- #define kImmediateTimeout 0
-
- struct ThreadList
- {
- short numThreads;
- ThreadID threads[1];
- };
-
- typedef struct ThreadList ThreadList, *ThreadListPtr, **ThreadListHdl;
-
- #define sizeofThreadList(numthreads) (sizeof(ThreadList) + ((numthreads)-1)*sizeof(ThreadID))
-
- typedef pascal OSErr (*AESpecialHandlerProcPtr)(AppleEvent *theAppleEvent);
-
- enum {
- uppAESpecialHandlerProcInfo = kPascalStackBased
- | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
- | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(AppleEvent*)))
- };
-
- #if USESROUTINEDESCRIPTORS
- typedef UniversalProcPtr AESpecialHandlerUPP;
-
- #define CallAESpecialHandlerProc(userRoutine, theAppleEvent, reply, handlerRefcon) \
- CallUniversalProc((UniversalProcPtr)(userRoutine), uppAESpecialHandlerProcInfo, (theAppleEvent), (reply), (handlerRefcon))
- #define NewAESpecialHandlerProc(userRoutine) \
- (AESpecialHandlerUPP) NewRoutineDescriptor((ProcPtr)(userRoutine), uppAESpecialHandlerProcInfo, GetCurrentISA())
- #else
- //typedef AESpecialHandlerProcPtr AESpecialHandlerUPP;
- typedef ProcPtr AESpecialHandlerUPP;
-
- #define CallAESpecialHandlerProc(userRoutine, theAppleEvent, reply, handlerRefcon) \
- (*(userRoutine))((theAppleEvent), (reply), (handlerRefcon))
- #define NewAESpecialHandlerProc(userRoutine) \
- (AESpecialHandlerUPP)(userRoutine)
- #endif
-
- AESpecialHandlerUPP gDoThreadBlockUPP = nil;
- AESpecialHandlerUPP gDoThreadUnblockUPP = nil;
- AESpecialHandlerUPP gIsFutureBlockUPP = nil;
-
-
- pascal OSErr InitFutures(void)
- {
- OSErr err;
- long aResponse;
-
- gDoThreadBlockUPP = NewAESpecialHandlerProc(DoThreadBlock);
- gDoThreadUnblockUPP = NewAESpecialHandlerProc(DoThreadUnblock);
- gIsFutureBlockUPP = NewAESpecialHandlerProc(DoThreadBlock);
-
- err = Gestalt(gestaltThreadMgrAttr, &aResponse);
-
- if (err == noErr)
- err = AEInstallSpecialHandler('blck', gDoThreadBlockUPP, false);
- if (err == noErr)
- err = AEInstallSpecialHandler('unbk', gDoThreadUnblockUPP, false);
-
- return err;
- }
-
- pascal OSErr DoThreadBlock(AppleEvent* message)
- {
- OSErr err;
- DescType actualType;
- Size actualSize;
- ThreadListHdl threadList;
- ThreadID currentThread;
-
- // Apparently the current thread needs to access some information, which
- // is really a future. We need to see if there is already a list of threads
- // blocked on this message. If there isn't, create an empty list. Add
- // the current thread to the list. Sleep the current thread.
-
-
- err = GetCurrentThread(¤tThread);
-
- if (err == noErr)
- {
- err = AEGetAttributePtr(message, kAERefconAttribute, typeLongInteger, &actualType,
- (Ptr) &threadList, sizeof(threadList), &actualSize);
-
- if (err == errAEDescNotFound || err == noErr && !threadList)
- {
- // If we can't find a waiting thread list, then create one containing
- // just ourself and put it back in the message
-
- threadList = (ThreadListHdl) NewHandle(sizeofThreadList(1));
-
- err = MemError();
-
- if (err == noErr)
- {
- (**threadList).numThreads = 1;
- (**threadList).threads[0] = currentThread;
-
- err = AEPutAttributePtr(message, kAERefconAttribute, typeLongInteger,
- (Ptr) &threadList, sizeof(threadList));
- }
- }
- else if (err == noErr)
- {
- // Otherwise just append ourself onto the existing list
-
- short numWaiting = (**threadList).numThreads;
-
- SetHandleSize((Handle) threadList, sizeofThreadList(numWaiting+1));
-
- err = MemError();
-
- if (err == noErr)
- {
- (**threadList).threads[numWaiting] = currentThread;
- (**threadList).numThreads = ++numWaiting;
- }
- }
- }
-
- // If there was an error don't block
-
- if (err == noErr)
- err = SetThreadState(currentThread, kStoppedThreadState, kNoThreadID);
-
- return err;
- }
-
- pascal OSErr DoThreadUnblock(AppleEvent* message)
- {
- OSErr err;
- DescType actualType;
- Size actualSize;
- ThreadState blockedThreadState;
- ThreadListHdl threadList;
-
- // This message has just turned real. If there is a list of threads blocked
- // because they tried to access the data, walk through the list of blocked
- // threads waking and deallocating the list element as you go.
-
- err = AEGetAttributePtr(message, kAERefconAttribute, typeLongInteger, &actualType,
- (Ptr) &threadList, sizeof(threadList), &actualSize);
-
- if (err == errAEDescNotFound)
- {
- // It's possible that this unblocking handler will get called for ALL replies
- // to apple events, not just futures. If that's the case, then getting
- // the above error is not really an error. Clear it and just return.
-
- err = noErr;
- }
- else if (err == noErr && threadList)
- {
- // If there's a waiting list, make all threads in it ready for execution
- // We won't report errors inside the loop because:
- // 1) one of the threads might have been disposed of
- // 2) there's nothing we can do to recover at this point
- // 3) the important thing is to wake up all remaining threads.
-
- ThreadID* nextThread = (**threadList).threads;
- short numWaiting = (**threadList).numThreads;
-
- while (--numWaiting >= 0)
- {
- ThreadID blockedThread = *nextThread++;
- OSErr err1 = GetThreadState(blockedThread, &blockedThreadState);
-
- if (err1 == noErr && blockedThreadState == kStoppedThreadState)
- {
- err1 = SetThreadState(blockedThread, kReadyThreadState, kNoThreadID);
- }
- }
-
- // Now free the list handle (and take it out of the refCon just to be safe)
-
- DisposeHandle((Handle) threadList);
-
- threadList = nil;
-
- err = AEPutAttributePtr(message, kAERefconAttribute, typeLongInteger,
- (Ptr) &threadList, sizeof(threadList));
-
- }
-
- return err;
- }
-
- pascal OSErr BlockUntilReal(AppleEvent* message)
- {
- OSErr err;
- AERecord nonExistentParameter;
-
- err = AEGetParamDesc(message, kAENonexistantAttribute,
- typeAERecord, &nonExistentParameter);
-
- if (err == errAEDescNotFound)
- err = noErr;
-
- return err;
- }
-
- pascal Boolean IsFuture(AppleEvent* message)
- {
- OSErr err;
- AESpecialHandlerUPP oldBlockingProc;
- Boolean isFuture = false;
-
- err = AEGetSpecialHandler('blck', &oldBlockingProc, false);
-
- if (err == noErr)
- {
- err = AEInstallSpecialHandler('blck', gIsFutureBlockUPP, false);
-
- if (err == noErr)
- {
- if (BlockUntilReal(message) == errAEReplyNotArrived)
- {
- isFuture = true;
- }
-
- err = AEInstallSpecialHandler('blck', oldBlockingProc, false);
- }
- }
-
- return isFuture;
- }
-
- pascal OSErr IsFutureBlock(AppleEvent* /*message*/)
- {
- return noErr;
- }
-
- pascal OSErr Ask(AppleEvent* question, AppleEvent* answer)
- {
- // Send the question with an immediate timeout.
-
- OSErr err = AESend(question, answer, kAEWaitReply, kAENormalPriority,
- kImmediateTimeout, nil, nil);
-
- if (err == errAETimeout)
- err = noErr;
-
- return err;
- }
-